home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume26 / shadow / part04 < prev    next >
Encoding:
Text File  |  1991-11-24  |  54.3 KB  |  2,449 lines

  1. Newsgroups: comp.sources.misc
  2. From: jfh@rpp386.Cactus.ORG (John F Haugh II)
  3. Subject:  v26i057:  shadow - Shadow Password Suite, Part04/11
  4. Message-ID: <1991Nov24.185012.20145@sparky.imd.sterling.com>
  5. X-Md4-Signature: c03114fe66a9e8b90c57c4303280b825
  6. Date: Sun, 24 Nov 1991 18:50:12 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: jfh@rpp386.Cactus.ORG (John F Haugh II)
  10. Posting-number: Volume 26, Issue 57
  11. Archive-name: shadow/part04
  12. Environment: UNIX
  13. Supersedes: shadow-2: Volume 06, Issue 22-24
  14.  
  15. #! /bin/sh
  16. # into a shell via "sh file" or similar.  To overwrite existing files,
  17. # type "sh file -c".
  18. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  19. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  20. # Contents:  chfn.c gpmain.c newusers.c patchlevel.h sgroupio.c
  21. # Wrapped by kent@sparky on Sun Nov 24 11:03:41 1991
  22. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  23. echo If this archive is complete, you will see the following message:
  24. echo '          "shar: End of archive 4 (of 11)."'
  25. if test -f 'chfn.c' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'chfn.c'\"
  27. else
  28.   echo shar: Extracting \"'chfn.c'\" \(12441 characters\)
  29.   sed "s/^X//" >'chfn.c' <<'END_OF_FILE'
  30. X/*
  31. X * Copyright 1989, 1990, 1991, John F. Haugh II
  32. X * All rights reserved.
  33. X *
  34. X * Permission is granted to copy and create derivative works for any
  35. X * non-commercial purpose, provided this copyright notice is preserved
  36. X * in all copies of source code, or included in human readable form
  37. X * and conspicuously displayed on all copies of object code or
  38. X * distribution media.
  39. X */
  40. X
  41. X#include <sys/types.h>
  42. X#include <stdio.h>
  43. X#include <fcntl.h>
  44. X#include <signal.h>
  45. X
  46. X#ifndef    lint
  47. Xstatic    char    sccsid[] = "@(#)chfn.c    3.7    10:14:35    8/15/91";
  48. X#endif
  49. X
  50. X/*
  51. X * Set up some BSD defines so that all the BSD ifdef's are
  52. X * kept right here 
  53. X */
  54. X
  55. X#ifndef    BSD
  56. X#include <string.h>
  57. X#include <memory.h>
  58. X#else
  59. X#include <strings.h>
  60. X#define    strchr    index
  61. X#define    strrchr    rindex
  62. X#endif
  63. X
  64. X#include "config.h"
  65. X#include "pwd.h"
  66. X
  67. X#ifdef    USE_SYSLOG
  68. X#include <syslog.h>
  69. X
  70. X#ifndef    LOG_WARN
  71. X#define    LOG_WARN LOG_WARNING
  72. X#endif
  73. X#endif
  74. X
  75. X/*
  76. X * Global variables.
  77. X */
  78. X
  79. Xchar    *Progname;
  80. Xchar    user[BUFSIZ];
  81. Xchar    fullnm[BUFSIZ];
  82. Xchar    roomno[BUFSIZ];
  83. Xchar    workph[BUFSIZ];
  84. Xchar    homeph[BUFSIZ];
  85. Xchar    slop[BUFSIZ];
  86. Xint    amroot;
  87. X
  88. X/*
  89. X * External identifiers
  90. X */
  91. X
  92. Xextern    int    optind;
  93. Xextern    char    *optarg;
  94. Xextern    struct    passwd    *getpwuid ();
  95. Xextern    struct    passwd    *getpwnam ();
  96. Xextern    char    *getlogin ();
  97. X#ifdef    NDBM
  98. Xextern    int    pw_dbm_mode;
  99. X#endif
  100. X
  101. X/*
  102. X * #defines for messages.  This facilities foreign language conversion
  103. X * since all messages are defined right here.
  104. X */
  105. X
  106. X#define    USAGE \
  107. X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n"
  108. X#define    ADMUSAGE \
  109. X"Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
  110. X       [ -h home_ph ] [ -o other ] [ user ]\n"
  111. X#define    NOPERM        "%s: Permission denied.\n"
  112. X#define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  113. X#define    INVALID_NAME    "%s: invalid name: \"%s\"\n"
  114. X#define    INVALID_ROOM    "%s: invalid room number: \"%s\"\n"
  115. X#define    INVALID_WORKPH    "%s: invalid work phone: \"%s\"\n"
  116. X#define    INVALID_HOMEPH    "%s: invalid home phone: \"%s\"\n"
  117. X#define    INVALID_OTHER    "%s: \"%s\" contains illegal characters\n"
  118. X#define    INVALID_FIELDS    "%s: fields too long\n"
  119. X#define    NEWFIELDSMSG    "Changing the user information for %s\n"
  120. X#define    NEWFIELDSMSG2 \
  121. X"Enter the new value, or press return for the default\n\n"
  122. X#define    NEWNAME        "Full Name"
  123. X#define    NEWROOM        "Room Number"
  124. X#define    NEWWORKPHONE    "Work Phone"
  125. X#define    NEWHOMEPHONE    "Home Phone"
  126. X#define    NEWSLOP        "Other"
  127. X#define    UNKUSER        "%s: Unknown user %s\n"
  128. X#define    PWDBUSY        "Cannot lock the password file; try again later.\n"
  129. X#define    PWDBUSY2    "can't lock /etc/passwd\n"
  130. X#define    OPNERROR    "Cannot open the password file.\n"
  131. X#define    OPNERROR2    "can't open /etc/passwd\n"
  132. X#define    UPDERROR    "Error updating the password entry.\n"
  133. X#define    UPDERROR2    "error updating passwd entry\n"
  134. X#define    DBMERROR    "Error updating the DBM password entry.\n"
  135. X#define    DBMERROR2    "error updating DBM passwd entry.\n"
  136. X#define    NOTROOT        "Cannot change ID to root.\n"
  137. X#define    NOTROOT2    "can't setuid(0).\n"
  138. X#define    CLSERROR    "Cannot commit password file changes.\n"
  139. X#define    CLSERROR2    "can't rewrite /etc/passwd.\n"
  140. X#define    UNLKERROR    "Cannot unlock the password file.\n"
  141. X#define    UNLKERROR2    "can't unlock /etc/passwd.\n"
  142. X#define    CHGGECOS    "changed user `%s' information.\n"
  143. X
  144. X/*
  145. X * usage - print command line syntax and exit
  146. X */
  147. X
  148. Xvoid
  149. Xusage ()
  150. X{
  151. X    fprintf (stderr, amroot ? USAGE:ADMUSAGE, Progname);
  152. X    exit (1);
  153. X}
  154. X
  155. X/*
  156. X * new_fields - change the user's GECOS information interactively
  157. X *
  158. X * prompt the user for each of the four fields and fill in the fields
  159. X * from the user's response, or leave alone if nothing was entered.
  160. X */
  161. X
  162. Xnew_fields ()
  163. X{
  164. X    printf (NEWFIELDSMSG2);
  165. X
  166. X    change_field (fullnm, NEWNAME);
  167. X    change_field (roomno, NEWROOM);
  168. X    change_field (workph, NEWWORKPHONE);
  169. X    change_field (homeph, NEWHOMEPHONE);
  170. X
  171. X    if (amroot)
  172. X        change_field (slop, NEWSLOP);
  173. X}
  174. X
  175. X/*
  176. X * copy_field - get the next field from the gecos field
  177. X *
  178. X * copy_field copies the next field from the gecos field, returning a
  179. X * pointer to the field which follows, or NULL if there are no more
  180. X * fields.
  181. X */
  182. X
  183. Xchar *
  184. Xcopy_field (in, out, extra)
  185. Xchar    *in;            /* the current GECOS field */
  186. Xchar    *out;            /* where to copy the field to */
  187. Xchar    *extra;            /* fields with '=' get copied here */
  188. X{
  189. X    char    *cp;
  190. X
  191. X    while (in) {
  192. X        if (cp = strchr (in, ','))
  193. X            *cp++ = '\0';
  194. X
  195. X        if (! strchr (in, '='))
  196. X            break;
  197. X
  198. X        if (extra) {
  199. X            if (extra[0])
  200. X                strcat (extra, ",");
  201. X
  202. X            strcat (extra, in);
  203. X        }
  204. X        in = cp;
  205. X    }
  206. X    if (in && out)
  207. X        strcpy (out, in);
  208. X
  209. X    return cp;
  210. X}
  211. X
  212. X/*
  213. X * chfn - change a user's password file information
  214. X *
  215. X *    This command controls the GECOS field information in the
  216. X *    password file entry.
  217. X *
  218. X *    The valid options are
  219. X *
  220. X *    -f    full name
  221. X *    -r    room number
  222. X *    -w    work phone number
  223. X *    -h    home phone number
  224. X *    -o    other information (*)
  225. X *
  226. X *    (*) requires root permission to execute.
  227. X */
  228. X
  229. Xint
  230. Xmain (argc, argv)
  231. Xint    argc;
  232. Xchar    **argv;
  233. X{
  234. X    char    *cp;            /* temporary character pointer       */
  235. X    struct    passwd    *pw;        /* password file entry               */
  236. X    struct    passwd    pwent;        /* modified password file entry      */
  237. X    char    old_gecos[BUFSIZ];    /* buffer for old GECOS fields       */
  238. X    char    new_gecos[BUFSIZ];    /* buffer for new GECOS fields       */
  239. X    int    flag;            /* flag currently being processed    */
  240. X    int    fflg = 0;        /* -f - set full name                */
  241. X    int    rflg = 0;        /* -r - set room number              */
  242. X    int    wflg = 0;        /* -w - set work phone number        */
  243. X    int    hflg = 0;        /* -h - set home phone number        */
  244. X    int    oflg = 0;        /* -o - set other information        */
  245. X    int    i;            /* loop control variable             */
  246. X
  247. X    /*
  248. X     * This command behaves different for root and non-root
  249. X     * users.
  250. X     */
  251. X
  252. X    amroot = getuid () == 0;
  253. X#ifdef    NDBM
  254. X    pw_dbm_mode = O_RDWR;
  255. X#endif
  256. X
  257. X    /*
  258. X     * Get the program name.  The program name is used as a
  259. X     * prefix to most error messages.  It is also used as input
  260. X     * to the openlog() function for error logging.
  261. X     */
  262. X
  263. X    if (Progname = strrchr (argv[0], '/'))
  264. X        Progname++;
  265. X    else
  266. X        Progname = argv[0];
  267. X
  268. X#ifdef    USE_SYSLOG
  269. X    openlog (Progname, LOG_PID, LOG_AUTH);
  270. X#endif
  271. X
  272. X    /* 
  273. X     * The remaining arguments will be processed one by one and
  274. X     * executed by this command.  The name is the last argument
  275. X     * if it does not begin with a "-", otherwise the name is
  276. X     * determined from the environment and must agree with the
  277. X     * real UID.  Also, the UID will be checked for any commands
  278. X     * which are restricted to root only.
  279. X     */
  280. X
  281. X    while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
  282. X        switch (flag) {
  283. X            case 'f':
  284. X                fflg++;
  285. X                strcpy (fullnm, optarg);
  286. X                break;
  287. X            case 'r':
  288. X                rflg++;
  289. X                strcpy (roomno, optarg);
  290. X                break;
  291. X            case 'w':
  292. X                wflg++;
  293. X                strcpy (workph, optarg);
  294. X                break;
  295. X            case 'h':
  296. X                hflg++;
  297. X                strcpy (homeph, optarg);
  298. X                break;
  299. X            case 'o':
  300. X                if (amroot) {
  301. X                    oflg++;
  302. X                    strcpy (slop, optarg);
  303. X                    break;
  304. X                }
  305. X                fprintf (stderr, NOPERM, Progname);
  306. X#ifdef    USE_SYSLOG
  307. X                closelog ();
  308. X#endif
  309. X                exit (1);
  310. X            default:
  311. X                usage ();
  312. X        }
  313. X    }
  314. X
  315. X    /*
  316. X     * Get the name of the user to check.  It is either
  317. X     * the command line name, or the name getlogin()
  318. X     * returns.
  319. X     */
  320. X
  321. X    if (optind < argc) {
  322. X        strncpy (user, argv[optind], sizeof user);
  323. X        pw = getpwnam (user);
  324. X    } else if (cp = getlogin ()) {
  325. X        strncpy (user, cp, sizeof user);
  326. X        pw = getpwnam (user);
  327. X    } else {
  328. X        fprintf (stderr, WHOAREYOU, Progname);
  329. X#ifdef    USE_SYSLOG
  330. X        closelog ();
  331. X#endif
  332. X        exit (1);
  333. X    }
  334. X
  335. X    /*
  336. X     * Make certain there was a password entry for the
  337. X     * user.
  338. X     */
  339. X
  340. X    if (! pw) {
  341. X        fprintf (stderr, UNKUSER, Progname, user);
  342. X#ifdef    USE_SYSLOG
  343. X        closelog ();
  344. X#endif
  345. X        exit (1);
  346. X    }
  347. X
  348. X    /*
  349. X     * Non-privileged users are only allowed to change the
  350. X     * shell if the UID of the user matches the current
  351. X     * real UID.
  352. X     */
  353. X
  354. X    if (! amroot && pw->pw_uid != getuid ()) {
  355. X        fprintf (stderr, NOPERM, Progname);
  356. X#ifdef    USE_SYSLOG
  357. X        closelog ();
  358. X#endif
  359. X        exit (1);
  360. X    }
  361. X
  362. X    /*
  363. X     * Make a copy of the user's password file entry so it
  364. X     * can be modified without worrying about it be modified
  365. X     * elsewhere.
  366. X     */
  367. X
  368. X    pwent = *pw;
  369. X    pwent.pw_name = strdup (pw->pw_name);
  370. X    pwent.pw_passwd = strdup (pw->pw_passwd);
  371. X#ifdef    ATT_AGE
  372. X    pwent.pw_age = strdup (pw->pw_age);
  373. X#endif
  374. X#ifdef    ATT_COMMENT
  375. X    pwent.pw_comment = strdup (pw->pw_comment);
  376. X#endif
  377. X    pwent.pw_dir = strdup (pw->pw_dir);
  378. X    pwent.pw_shell = strdup (pw->pw_shell);
  379. X
  380. X    /*
  381. X     * Now get the full name.  It is the first comma separated field
  382. X     * in the GECOS field.
  383. X     */
  384. X
  385. X    strcpy (old_gecos, pw->pw_gecos);
  386. X    cp = copy_field (old_gecos, fflg ? (char *) 0:fullnm, slop);
  387. X
  388. X    /*
  389. X     * Now get the room number.  It is the next comma separated field,
  390. X     * if there is indeed one.
  391. X     */
  392. X
  393. X    if (cp)
  394. X        cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
  395. X
  396. X    /*
  397. X     * Now get the work phone number.  It is the third field.
  398. X     */
  399. X
  400. X    if (cp)
  401. X        cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
  402. X
  403. X    /*
  404. X     * Now get the home phone number.  It is the fourth field.
  405. X     */
  406. X
  407. X    if (cp)
  408. X        cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
  409. X
  410. X    /*
  411. X     * Anything left over is "slop".
  412. X     */
  413. X
  414. X    if (cp) {
  415. X        if (slop[0])
  416. X            strcat (slop, ",");
  417. X
  418. X        strcat (slop, cp);
  419. X    }
  420. X
  421. X    /*
  422. X     * If none of the fields were changed from the command line,
  423. X     * let the user interactively change them.
  424. X     */
  425. X
  426. X    if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg) {
  427. X        printf (NEWFIELDSMSG, user);
  428. X        new_fields ();
  429. X    }
  430. X
  431. X    /*
  432. X     * Check all of the fields for valid information
  433. X     */
  434. X
  435. X    if (valid_field (fullnm, ":,=")) {
  436. X        fprintf (stderr, INVALID_NAME, Progname, fullnm);
  437. X#ifdef    USE_SYSLOG
  438. X        closelog ();
  439. X#endif
  440. X        exit (1);
  441. X    }
  442. X    if (valid_field (roomno, ":,=")) {
  443. X        fprintf (stderr, INVALID_ROOM, Progname, roomno);
  444. X#ifdef    USE_SYSLOG
  445. X        closelog ();
  446. X#endif
  447. X        exit (1);
  448. X    }
  449. X    if (valid_field (workph, ":,=")) {
  450. X        fprintf (stderr, INVALID_WORKPH, Progname, workph);
  451. X#ifdef    USE_SYSLOG
  452. X        closelog ();
  453. X#endif
  454. X        exit (1);
  455. X    }
  456. X    if (valid_field (homeph, ":,=")) {
  457. X        fprintf (stderr, INVALID_HOMEPH, Progname, homeph);
  458. X#ifdef    USE_SYSLOG
  459. X        closelog ();
  460. X#endif
  461. X        exit (1);
  462. X    }
  463. X    if (valid_field (slop, ":")) {
  464. X        fprintf (stderr, INVALID_OTHER, Progname, slop);
  465. X#ifdef    USE_SYSLOG
  466. X        closelog ();
  467. X#endif
  468. X        exit (1);
  469. X    }
  470. X
  471. X    /*
  472. X     * Build the new GECOS field by plastering all the pieces together,
  473. X     * if they will fit ...
  474. X     */
  475. X
  476. X    if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
  477. X            strlen (homeph) + strlen (slop) > 80) {
  478. X        fprintf (stderr, INVALID_FIELDS, Progname);
  479. X#ifdef    USE_SYSLOG
  480. X        closelog ();
  481. X#endif
  482. X        exit (1);
  483. X    }
  484. X    sprintf (new_gecos, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
  485. X    if (slop[0]) {
  486. X        strcat (new_gecos, ",");
  487. X        strcat (new_gecos, slop);
  488. X    }
  489. X    pwent.pw_gecos = new_gecos;
  490. X    pw = &pwent;
  491. X
  492. X    /*
  493. X     * Before going any further, raise the ulimit to prevent
  494. X     * colliding into a lowered ulimit, and set the real UID
  495. X     * to root to protect against unexpected signals.  Any
  496. X     * keyboard signals are set to be ignored.
  497. X     */
  498. X
  499. X    ulimit (2, 30000);
  500. X    if (setuid (0)) {
  501. X        fprintf (stderr, NOTROOT);
  502. X#ifdef    USE_SYSLOG
  503. X        syslog (LOG_ERR, NOTROOT2);
  504. X        closelog ();
  505. X#endif
  506. X        exit (1);
  507. X    }
  508. X    signal (SIGHUP, SIG_IGN);
  509. X    signal (SIGINT, SIG_IGN);
  510. X    signal (SIGQUIT, SIG_IGN);
  511. X#ifdef    SIGTSTP
  512. X    signal (SIGTSTP, SIG_IGN);
  513. X#endif
  514. X
  515. X    /*
  516. X     * The passwd entry is now ready to be committed back to
  517. X     * the password file.  Get a lock on the file and open it.
  518. X     */
  519. X
  520. X    for (i = 0;i < 30;i++)
  521. X        if (pw_lock ())
  522. X            break;
  523. X
  524. X    if (i == 30) {
  525. X        fprintf (stderr, PWDBUSY);
  526. X#ifdef    USE_SYSLOG
  527. X        syslog (LOG_WARN, PWDBUSY2);
  528. X        closelog ();
  529. X#endif
  530. X        exit (1);
  531. X    }
  532. X    if (! pw_open (O_RDWR)) {
  533. X        fprintf (stderr, OPNERROR);
  534. X        (void) pw_unlock ();
  535. X#ifdef    USE_SYSLOG
  536. X        syslog (LOG_ERR, OPNERROR2);
  537. X        closelog ();
  538. X#endif
  539. X        exit (1);
  540. X    }
  541. X
  542. X    /*
  543. X     * Update the passwd file entry.  If there is a DBM file,
  544. X     * update that entry as well.
  545. X     */
  546. X
  547. X    if (! pw_update (pw)) {
  548. X        fprintf (stderr, UPDERROR);
  549. X        (void) pw_unlock ();
  550. X#ifdef    USE_SYSLOG
  551. X        syslog (LOG_ERR, UPDERROR2);
  552. X        closelog ();
  553. X#endif
  554. X        exit (1);
  555. X    }
  556. X#if defined(DBM) || defined(NDBM)
  557. X    if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
  558. X        fprintf (stderr, DBMERROR);
  559. X        (void) pw_unlock ();
  560. X#ifdef    USE_SYSLOG
  561. X        syslog (LOG_ERR, DBMERROR2);
  562. X        closelog ();
  563. X#endif
  564. X        exit (1);
  565. X    }
  566. X    endpwent ();
  567. X#endif
  568. X
  569. X    /*
  570. X     * Changes have all been made, so commit them and unlock the
  571. X     * file.
  572. X     */
  573. X
  574. X    if (! pw_close ()) {
  575. X        fprintf (stderr, CLSERROR);
  576. X        (void) pw_unlock ();
  577. X#ifdef    USE_SYSLOG
  578. X        syslog (LOG_ERR, CLSERROR2);
  579. X        closelog ();
  580. X#endif
  581. X        exit (1);
  582. X    }
  583. X    if (! pw_unlock ()) {
  584. X        fprintf (stderr, UNLKERROR);
  585. X#ifdef    USE_SYSLOG
  586. X        syslog (LOG_ERR, UNLKERROR2);
  587. X        closelog ();
  588. X#endif
  589. X        exit (1);
  590. X    }
  591. X#ifdef    USE_SYSLOG
  592. X    syslog (LOG_INFO, CHGGECOS, user);
  593. X    closelog ();
  594. X#endif
  595. X    exit (0);
  596. X}
  597. END_OF_FILE
  598.   if test 12441 -ne `wc -c <'chfn.c'`; then
  599.     echo shar: \"'chfn.c'\" unpacked with wrong size!
  600.   fi
  601.   # end of 'chfn.c'
  602. fi
  603. if test -f 'gpmain.c' -a "${1}" != "-c" ; then 
  604.   echo shar: Will not clobber existing file \"'gpmain.c'\"
  605. else
  606.   echo shar: Extracting \"'gpmain.c'\" \(12316 characters\)
  607.   sed "s/^X//" >'gpmain.c' <<'END_OF_FILE'
  608. X/*
  609. X * Copyright 1990, 1991, John F. Haugh II
  610. X * All rights reserved.
  611. X *
  612. X * Permission is granted to copy and create derivative works for any
  613. X * non-commercial purpose, provided this copyright notice is preserved
  614. X * in all copies of source code, or included in human readable form
  615. X * and conspicuously displayed on all copies of object code or
  616. X * distribution media.
  617. X */
  618. X
  619. X#include <sys/types.h>
  620. X#include <stdio.h>
  621. X#include "pwd.h"
  622. X#include "shadow.h"
  623. X#include <grp.h>
  624. X#include <fcntl.h>
  625. X#include <signal.h>
  626. X#include <errno.h>
  627. X#ifndef    BSD
  628. X#include <termio.h>
  629. X#ifdef SYS3
  630. X#include <sys/ioctl.h>
  631. X#endif
  632. X#include <string.h>
  633. X#ifndef    SYS3
  634. X#include <memory.h>
  635. X#endif
  636. X#else
  637. X#include <sgtty.h>
  638. X#include <strings.h>
  639. X#define    strchr    index
  640. X#define    strrchr    rindex
  641. X#endif
  642. X#include "config.h"
  643. X
  644. X#if !defined(BSD) || !defined(SUN)
  645. X#define    bzero(p,l) memset(p, 0, l)
  646. X#endif
  647. X
  648. X#ifndef    lint
  649. Xstatic    char    _sccsid[] = "@(#)gpmain.c    3.10    07:44:08    9/17/91";
  650. X#endif
  651. X
  652. Xchar    name[BUFSIZ];
  653. Xchar    pass[BUFSIZ];
  654. Xchar    pass2[BUFSIZ];
  655. X
  656. Xstruct    group    grent;
  657. X
  658. Xchar    *Prog;
  659. Xchar    *user;
  660. Xchar    *group;
  661. Xint    aflg;
  662. Xint    dflg;
  663. Xint    rflg;
  664. Xint    Rflg;
  665. X
  666. X#ifndef    RETRIES
  667. X#define    RETRIES    3
  668. X#endif
  669. X
  670. Xextern    char    *l64a ();
  671. Xextern    char    *crypt ();
  672. Xextern    char    *pw_encrypt ();
  673. Xextern    int    errno;
  674. Xextern    long    a64l ();
  675. Xextern    void    entry ();
  676. Xextern    time_t    time ();
  677. Xextern    char    *malloc ();
  678. Xextern    char    *getpass ();
  679. X#ifdef    NDBM
  680. Xextern    int    sg_dbm_mode;
  681. Xextern    int    gr_dbm_mode;
  682. X#endif
  683. X
  684. X/*
  685. X * usage - display usage message
  686. X */
  687. X
  688. Xvoid
  689. Xusage ()
  690. X{
  691. X    fprintf (stderr, "usage: %s [ -r|R ] group\n", Prog);
  692. X    fprintf (stderr, "       %s [ -a user ] group\n", Prog);
  693. X    fprintf (stderr, "       %s [ -d user ] group\n", Prog);
  694. X    exit (1);
  695. X}
  696. X
  697. X/*
  698. X * add_list - add a member to a list of group members
  699. X *
  700. X *    the array of member names is searched for the new member
  701. X *    name, and if not present it is added to a freshly allocated
  702. X *    list of users.
  703. X */
  704. X
  705. Xchar **
  706. Xadd_list (list, member)
  707. Xchar    **list;
  708. Xchar    *member;
  709. X{
  710. X    int    i;
  711. X    char    **tmp;
  712. X
  713. X    for (i = 0;list[i] != (char *) 0;i++)
  714. X        if (strcmp (list[i], member) == 0)
  715. X            return list;
  716. X
  717. X    if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
  718. X        return 0;
  719. X
  720. X    for (i = 0;list[i] != (char *) 0;i++)
  721. X        tmp[i] = list[i];
  722. X
  723. X    tmp[i++] = strdup (member);
  724. X    tmp[i] = (char *) 0;
  725. X
  726. X    return tmp;
  727. X}
  728. X
  729. X/*
  730. X * del_list - delete a group member from a list of members
  731. X *
  732. X *    del_list searches a list of group members, copying the
  733. X *    members which do not match "member" to a newly allocated
  734. X *    list.
  735. X */
  736. X
  737. Xchar **
  738. Xdel_list (list, member)
  739. Xchar    **list;
  740. Xchar    *member;
  741. X{
  742. X    int    i, j;
  743. X    char    **tmp;
  744. X
  745. X    for (j = i = 0;list[i] != (char *) 0;i++)
  746. X        if (strcmp (list[i], member))
  747. X            j++;
  748. X
  749. X    tmp = (char **) malloc ((j + 1) * sizeof member);
  750. X
  751. X    for (j = i = 0;list[i] != (char *) 0;i++)
  752. X        if (strcmp (list[i], member) != 0)
  753. X            tmp[j++] = list[i];
  754. X
  755. X    tmp[j] = (char *) 0;
  756. X
  757. X    return tmp;
  758. X}
  759. X
  760. Xint
  761. Xmain (argc, argv)
  762. Xint    argc;
  763. Xchar    **argv;
  764. X{
  765. X    extern    int    optind;
  766. X    extern    char    *optarg;
  767. X    int    flag;
  768. X    int    i;
  769. X    void    die ();
  770. X    char    *cp;
  771. X    char    *getlogin ();
  772. X    char    *getpass ();
  773. X    int    amroot;
  774. X    int    retries;
  775. X    int    ruid = getuid();
  776. X    struct    group    *gr = 0;
  777. X    struct    group    *getgrnam ();
  778. X    struct    group    *sgetgrent ();
  779. X    struct    sgrp    *sg = 0;
  780. X    struct    sgrp    sgent;
  781. X    struct    sgrp    *getsgnam ();
  782. X    struct    passwd    *pw = 0;
  783. X    struct    passwd    *getpwuid ();
  784. X    struct    passwd    *getpwnam ();
  785. X
  786. X    /*
  787. X     * Make a note of whether or not this command was invoked
  788. X     * by root.  This will be used to bypass certain checks
  789. X     * later on.  Also, set the real user ID to match the
  790. X     * effective user ID.  This will prevent the invoker from
  791. X     * issuing signals which would interfer with this command.
  792. X     */
  793. X
  794. X    amroot = getuid () == 0;
  795. X#ifdef    NDBM
  796. X    sg_dbm_mode = O_RDWR;
  797. X    gr_dbm_mode = O_RDWR;
  798. X#endif
  799. X    setuid (geteuid ());
  800. X    Prog = argv[0];
  801. X    setbuf (stdout, (char *) 0);
  802. X    setbuf (stderr, (char *) 0);
  803. X
  804. X    while ((flag = getopt (argc, argv, "a:d:grR")) != EOF) {
  805. X        switch (flag) {
  806. X            case 'a':    /* add a user */
  807. X                aflg++;
  808. X                user = optarg;
  809. X                break;
  810. X            case 'd':    /* delete a user */
  811. X                dflg++;
  812. X                user = optarg;
  813. X                break;
  814. X            case 'g':    /* no-op from normal password */
  815. X                break;
  816. X            case 'r':    /* remove group password */
  817. X                rflg++;
  818. X                break;
  819. X            case 'R':    /* restrict group password */
  820. X                Rflg++;
  821. X                break;
  822. X            default:
  823. X                usage ();
  824. X        }
  825. X    }
  826. X
  827. X    /*
  828. X     * Make sure exclusive flags are exclusive
  829. X     */
  830. X
  831. X    if (aflg + dflg + rflg + Rflg > 1)
  832. X        usage ();
  833. X
  834. X    /*
  835. X     * Unless the mode is -a, -d or -r, the input and output must
  836. X     * both be a tty.  The typical keyboard signals are caught
  837. X     * so the termio modes can be restored.
  838. X     */
  839. X
  840. X    if (! aflg && ! dflg && ! rflg && ! Rflg) {
  841. X        if (! isatty (0) || ! isatty (1))
  842. X            exit (1);
  843. X
  844. X        die (0);            /* save tty modes */
  845. X
  846. X        signal (SIGHUP, die);
  847. X        signal (SIGINT, die);
  848. X        signal (SIGQUIT, die);
  849. X        signal (SIGTERM, die);
  850. X    }
  851. X
  852. X    /*
  853. X     * Determine the name of the user that invoked this command.
  854. X     * This is really hit or miss because there are so many ways
  855. X     * that command can be executed and so many ways to trip up
  856. X     * the routines that report the user name.
  857. X     */
  858. X
  859. X    if ((cp = getlogin ()) && (pw = getpwnam (cp)) && pw->pw_uid == ruid) {
  860. X                    /* need user name */
  861. X        (void) strcpy (name, cp);
  862. X    } else if (pw = getpwuid (ruid)) /* get it from password file */
  863. X        strcpy (name, pw->pw_name);
  864. X    else {                /* can't find user name! */
  865. X        fprintf (stderr, "Who are you?\n");
  866. X        exit (1);
  867. X    }
  868. X    if (! (pw = getpwnam (name)))
  869. X        goto failure;        /* can't get my name ... */
  870. X        
  871. X    /*
  872. X     * Get the name of the group that is being affected.  The group
  873. X     * entry will be completely replicated so it may be modified
  874. X     * later on.
  875. X     */
  876. X
  877. X    if (! (group = argv[optind]))
  878. X        usage ();
  879. X
  880. X    if (! (gr = getgrnam (group))) {
  881. X        fprintf (stderr, "unknown group: %s\n", group);
  882. X        exit (1);
  883. X    }
  884. X    grent = *gr;
  885. X    grent.gr_name = strdup (gr->gr_name);
  886. X    grent.gr_passwd = strdup (gr->gr_passwd);
  887. X
  888. X    for (i = 0;gr->gr_mem[i];i++)
  889. X        ;
  890. X    grent.gr_mem = (char **) malloc ((i + 1) * sizeof (char *));
  891. X    for (i = 0;gr->gr_mem[i];i++)
  892. X        grent.gr_mem[i] = strdup (gr->gr_mem[i]);
  893. X    grent.gr_mem[i] = (char *) 0;
  894. X#ifdef    SHADOWGRP
  895. X    if (sg = getsgnam (group)) {
  896. X        sgent = *sg;
  897. X        sgent.sg_name = strdup (sg->sg_name);
  898. X        sgent.sg_passwd = strdup (sg->sg_name);
  899. X
  900. X        for (i = 0;sg->sg_mem[i];i++)
  901. X            ;
  902. X        sgent.sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
  903. X        for (i = 0;sg->sg_mem[i];i++)
  904. X            sgent.sg_mem[i] = strdup (sg->sg_mem[i]);
  905. X        sgent.sg_mem[i] = 0;
  906. X
  907. X        for (i = 0;sg->sg_adm[i];i++)
  908. X            ;
  909. X        sgent.sg_adm = (char **) malloc (sizeof (char *) * (i + 1));
  910. X        for (i = 0;sg->sg_adm[i];i++)
  911. X            sgent.sg_adm[i] = strdup (sg->sg_adm[i]);
  912. X        sgent.sg_adm[i] = 0;
  913. X    } else {
  914. X        sgent.sg_name = strdup (group);
  915. X        sgent.sg_passwd = grent.gr_passwd;
  916. X        grent.gr_passwd = "!";
  917. X
  918. X        for (i = 0;grent.gr_mem[i];i++)
  919. X            ;
  920. X        sgent.sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
  921. X        for (i = 0;grent.gr_mem[i];i++)
  922. X            sgent.sg_mem[i] = strdup (grent.gr_mem[i]);
  923. X        sgent.sg_mem[i] = 0;
  924. X
  925. X        sgent.sg_adm = (char **) malloc (sizeof (char *) * 2);
  926. X        if (sgent.sg_mem[0]) {
  927. X            sgent.sg_adm[0] = strdup (sgent.sg_mem[0]);
  928. X            sgent.sg_adm[1] = 0;
  929. X        } else
  930. X            sgent.sg_adm[0] = 0;
  931. X
  932. X        sg = &sgent;
  933. X    }
  934. X#endif
  935. X
  936. X    /*
  937. X     * The policy for changing a group is that 1) you must be root
  938. X     * or 2) you must be the first listed member of the group.  The
  939. X     * first listed member of a group can do anything to that group
  940. X     * that the root user can.
  941. X     */
  942. X
  943. X    if (! amroot) {
  944. X        if (grent.gr_mem[0] == (char *) 0)
  945. X            goto failure;
  946. X
  947. X        if (strcmp (grent.gr_mem[0], name) != 0)
  948. X            goto failure;
  949. X    }
  950. X
  951. X    /*
  952. X     * Removing a password is straight forward.  Just set the
  953. X     * password field to a "".
  954. X     */
  955. X
  956. X    if (rflg) {
  957. X        grent.gr_passwd = "";
  958. X#ifdef    SHADOWGRP
  959. X        sgent.sg_passwd = "";
  960. X#endif
  961. X        goto output;
  962. X    } else if (Rflg) {
  963. X        grent.gr_passwd = "!";
  964. X#ifdef    SHADOWGRP
  965. X        sgent.sg_passwd = "!";
  966. X#endif
  967. X        goto output;
  968. X    }
  969. X
  970. X    /*
  971. X     * Adding a member to a member list is pretty straightforward
  972. X     * as well.  Call the appropriate routine and split.
  973. X     */
  974. X
  975. X    if (aflg) {
  976. X        if (getpwnam (user) == (struct passwd *) 0) {
  977. X            fprintf (stderr, "%s: unknown user %s\n", Prog, user);
  978. X            exit (1);
  979. X        }
  980. X        printf ("Adding user %s to group %s\n", user, group);
  981. X        grent.gr_mem = add_list (grent.gr_mem, user);
  982. X#ifdef    SHADOWGRP
  983. X        sgent.sg_mem = add_list (sgent.sg_mem, user);
  984. X#endif
  985. X        goto output;
  986. X    }
  987. X
  988. X    /*
  989. X     * Removing a member from the member list is the same deal
  990. X     * as adding one, except the routine is different.
  991. X     */
  992. X
  993. X    if (dflg) {
  994. X        int    removed = 0;
  995. X
  996. X        for (i = 0;grent.gr_mem[i];i++)
  997. X            if (strcmp (user, grent.gr_mem[i]) == 0)
  998. X                break;
  999. X
  1000. X        printf ("Removing user %s from group %s\n", user, group);
  1001. X
  1002. X        if (grent.gr_mem[i] != (char *) 0) {
  1003. X            removed = 1;
  1004. X            grent.gr_mem = del_list (grent.gr_mem, user);
  1005. X        }
  1006. X#ifdef    SHADOWGRP
  1007. X        for (i = 0;sgent.sg_mem[i];i++)
  1008. X            if (strcmp (user, sgent.sg_mem[i]) == 0)
  1009. X                break;
  1010. X
  1011. X        if (sgent.sg_mem[i] != (char *) 0) {
  1012. X            removed = 1;
  1013. X            sgent.sg_mem = del_list (sgent.sg_mem, user);
  1014. X        }
  1015. X#endif
  1016. X        if (! removed) {
  1017. X            fprintf (stderr, "%s: unknown member %s\n", Prog, user);
  1018. X            exit (1);
  1019. X        }
  1020. X        goto output;
  1021. X    }
  1022. X
  1023. X    /*
  1024. X     * A new password is to be entered and it must be encrypted,
  1025. X     * etc.  The password will be prompted for twice, and both
  1026. X     * entries must be identical.  There is no need to validate
  1027. X     * the old password since the invoker is either the group
  1028. X     * owner, or root.
  1029. X     */
  1030. X
  1031. X    printf ("Changing the password for group %s\n", group);
  1032. X
  1033. X    for (retries = 0;retries < RETRIES;retries++) {
  1034. X        if (! (cp = getpass ("New Password:")))
  1035. X            exit (1);
  1036. X        else {
  1037. X            strcpy (pass, cp);
  1038. X            bzero (cp, strlen (cp));
  1039. X        }
  1040. X        if (! (cp = getpass ("Re-enter new password:")))
  1041. X            exit (1);
  1042. X        else {
  1043. X            strcpy (pass2, cp);
  1044. X            bzero (cp, strlen (cp));
  1045. X        }
  1046. X        if (strcmp (pass, pass2) == 0)
  1047. X            break;
  1048. X
  1049. X        bzero (pass, sizeof pass);
  1050. X        bzero (pass2, sizeof pass2);
  1051. X
  1052. X        if (retries + 1 < RETRIES)
  1053. X            puts ("They don't match; try again");
  1054. X    }
  1055. X    bzero (pass2, sizeof pass2);
  1056. X
  1057. X    if (retries == RETRIES) {
  1058. X        fprintf (stderr, "%s: Try again later\n", Prog);
  1059. X        exit (1);
  1060. X    }
  1061. X#ifdef    SHADOWGRP
  1062. X    sgent.sg_passwd = pw_encrypt (pass, (char *) 0);
  1063. X#else
  1064. X    grent.gr_passwd = pw_encrypt (pass, (char *) 0);
  1065. X#endif
  1066. X    bzero (pass, sizeof pass);
  1067. X
  1068. X    /*
  1069. X     * This is the common arrival point to output the new group
  1070. X     * file.  The freshly crafted entry is in allocated space.
  1071. X     * The group file will be locked and opened for writing.  The
  1072. X     * new entry will be output, etc.
  1073. X     */
  1074. X
  1075. Xoutput:
  1076. X    signal (SIGHUP, SIG_IGN);
  1077. X    signal (SIGINT, SIG_IGN);
  1078. X    signal (SIGQUIT, SIG_IGN);
  1079. X
  1080. X    if (! gr_lock ()) {
  1081. X        fprintf (stderr, "%s: can't get lock\n", Prog);
  1082. X        exit (1);
  1083. X    }
  1084. X#ifdef    SHADOWGRP
  1085. X    if (! sgr_lock ()) {
  1086. X        fprintf (stderr, "%s: can't get shadow lock\n", Prog);
  1087. X        exit (1);
  1088. X    }
  1089. X#endif
  1090. X    if (! gr_open (O_RDWR)) {
  1091. X        fprintf (stderr, "%s: can't open file\n", Prog);
  1092. X        exit (1);
  1093. X    }
  1094. X#ifdef    SHADOWGRP
  1095. X    if (! sgr_open (O_RDWR)) {
  1096. X        fprintf (stderr, "%s: can't open shadow file\n", Prog);
  1097. X        exit (1);
  1098. X    }
  1099. X#endif
  1100. X    if (! gr_update (&grent)) {
  1101. X        fprintf (stderr, "%s: can't update entry\n", Prog);
  1102. X        exit (1);
  1103. X    }
  1104. X#ifdef    SHADOWGRP
  1105. X    if (! sgr_update (&sgent)) {
  1106. X        fprintf (stderr, "%s: can't update shadow entry\n", Prog);
  1107. X        exit (1);
  1108. X    }
  1109. X#endif
  1110. X    if (! gr_close ()) {
  1111. X        fprintf (stderr, "%s: can't re-write file\n", Prog);
  1112. X        exit (1);
  1113. X    }
  1114. X#ifdef    SHADOWGRP
  1115. X    if (! sgr_close ()) {
  1116. X        fprintf (stderr, "%s: can't re-write shadow file\n", Prog);
  1117. X        exit (1);
  1118. X    }
  1119. X    (void) sgr_unlock ();
  1120. X#endif
  1121. X    if (! gr_unlock ()) {
  1122. X        fprintf (stderr, "%s: can't unlock file\n", Prog);
  1123. X        exit (1);
  1124. X    }
  1125. X#ifdef    NDBM
  1126. X    if (access ("/etc/group.pag", 0) == 0 && ! gr_dbm_update (&grent)) {
  1127. X        fprintf (stderr, "%s: can't update DBM files\n", Prog);
  1128. X        exit (1);
  1129. X    }
  1130. X    endgrent ();
  1131. X#ifdef    SHADOWGRP
  1132. X    if (access ("/etc/gshadow.pag", 0) == 0 && ! sgr_dbm_update (&sgent)) {
  1133. X        fprintf (stderr, "%s: can't update DBM shadow files\n", Prog);
  1134. X        exit (1);
  1135. X    }
  1136. X    endsgent ();
  1137. X#endif
  1138. X#endif
  1139. X    exit (0);
  1140. X    /*NOTREACHED*/
  1141. X
  1142. Xfailure:
  1143. X    fprintf (stderr, "Permission denied.\n");
  1144. X    exit (1);
  1145. X    /*NOTREACHED*/
  1146. X}
  1147. X
  1148. X/*
  1149. X * die - set or reset termio modes.
  1150. X *
  1151. X *    die() is called before processing begins.  signal() is then
  1152. X *    called with die() as the signal handler.  If signal later
  1153. X *    calls die() with a signal number, the terminal modes are
  1154. X *    then reset.
  1155. X */
  1156. X
  1157. Xvoid    die (killed)
  1158. Xint    killed;
  1159. X{
  1160. X#ifdef    BSD
  1161. X    static    struct    sgtty    sgtty;
  1162. X
  1163. X    if (killed)
  1164. X        stty (0, &sgtty);
  1165. X    else
  1166. X        gtty (0, &sgtty);
  1167. X#else
  1168. X    static    struct    termio    sgtty;
  1169. X
  1170. X    if (killed)
  1171. X        ioctl (0, TCSETA, &sgtty);
  1172. X    else
  1173. X        ioctl (0, TCGETA, &sgtty);
  1174. X#endif
  1175. X    if (killed) {
  1176. X        putchar ('\n');
  1177. X        fflush (stdout);
  1178. X        exit (killed);
  1179. X    }
  1180. X}
  1181. END_OF_FILE
  1182.   if test 12316 -ne `wc -c <'gpmain.c'`; then
  1183.     echo shar: \"'gpmain.c'\" unpacked with wrong size!
  1184.   fi
  1185.   # end of 'gpmain.c'
  1186. fi
  1187. if test -f 'newusers.c' -a "${1}" != "-c" ; then 
  1188.   echo shar: Will not clobber existing file \"'newusers.c'\"
  1189. else
  1190.   echo shar: Extracting \"'newusers.c'\" \(13144 characters\)
  1191.   sed "s/^X//" >'newusers.c' <<'END_OF_FILE'
  1192. X/*
  1193. X * Copyright 1990, 1991, John F. Haugh II
  1194. X * All rights reserved.
  1195. X *
  1196. X * Permission is granted to copy and create derivative works for any
  1197. X * non-commercial purpose, provided this copyright notice is preserved
  1198. X * in all copies of source code, or included in human readable form
  1199. X * and conspicuously displayed on all copies of object code or
  1200. X * distribution media.
  1201. X *
  1202. X *    newusers - create users from a batch file
  1203. X *
  1204. X *    newusers creates a collection of entries in /etc/passwd
  1205. X *    and related files by reading a passwd-format file and
  1206. X *    adding entries in the related directories.
  1207. X */
  1208. X
  1209. X#include <stdio.h>
  1210. X#include "pwd.h"
  1211. X#include <grp.h>
  1212. X#include <fcntl.h>
  1213. X#include <string.h>
  1214. X#include "config.h"
  1215. X#ifdef    SHADOWPWD
  1216. X#include "shadow.h"
  1217. X#endif
  1218. X
  1219. X#ifndef    lint
  1220. Xstatic    char    sccsid[] = "@(#)newusers.c    3.5    07:44:18    9/17/91";
  1221. X#endif
  1222. X
  1223. Xchar    *Prog;
  1224. X
  1225. Xextern    char    *pw_encrypt();
  1226. Xextern    char    *malloc();
  1227. X
  1228. Xint    pw_lock(), gr_lock();
  1229. Xint    pw_open(), gr_open();
  1230. Xstruct    passwd    *pw_locate(), *pw_next();
  1231. Xstruct    group    *gr_locate(), *gr_next();
  1232. Xint    pw_update(), gr_update();
  1233. Xint    pw_close(), gr_close();
  1234. Xint    pw_unlock(), gr_unlock();
  1235. Xextern    int    getdef_num();
  1236. X
  1237. X#ifdef    SHADOWPWD
  1238. Xint    spw_lock(), spw_open(), spw_update(), spw_close(), spw_unlock();
  1239. Xstruct    spwd    *spw_locate(), *spw_next();
  1240. X#endif
  1241. X
  1242. X#ifndef    MKDIR
  1243. X
  1244. X/*
  1245. X * mkdir - for those of us with no mkdir() system call.
  1246. X */
  1247. X
  1248. Xmkdir (dir, mode)
  1249. Xchar    *dir;
  1250. Xint    mode;
  1251. X{
  1252. X    int    mask;
  1253. X    int    status;
  1254. X    int    pid;
  1255. X    int    i;
  1256. X
  1257. X    mode = (~mode & 0777);
  1258. X    mask = umask (mode);
  1259. X    if ((pid = fork ()) == 0) {
  1260. X        execl ("/bin/mkdir", "mkdir", dir, (char *) 0);
  1261. X        perror ("/bin/mkdir");
  1262. X        _exit (1);
  1263. X    } else {
  1264. X        while ((i = wait (&status)) != pid && i != -1)
  1265. X            ;
  1266. X    }
  1267. X    umask (mask);
  1268. X    return status;
  1269. X}
  1270. X#endif
  1271. X
  1272. X/*
  1273. X * usage - display usage message and exit
  1274. X */
  1275. X
  1276. Xusage ()
  1277. X{
  1278. X    fprintf (stderr, "Usage: %s [ input ]\n", Prog);
  1279. X    exit (1);
  1280. X}
  1281. X
  1282. X/*
  1283. X * add_group - create a new group or add a user to an existing group
  1284. X */
  1285. X
  1286. Xint
  1287. Xadd_group (name, gid, ngid)
  1288. Xchar    *name;
  1289. Xchar    *gid;
  1290. Xint    *ngid;
  1291. X{
  1292. X    struct    passwd    *pwd;
  1293. X    struct    group    *grp;
  1294. X    struct    group    grent;
  1295. X    char    *members[2];
  1296. X    int    i;
  1297. X
  1298. X    /*
  1299. X     * Start by seeing if the named group already exists.  This
  1300. X     * will be very easy to deal with if it does.
  1301. X     */
  1302. X
  1303. X    if (grp = gr_locate (gid)) {
  1304. Xadd_member:
  1305. X        grent = *grp;
  1306. X        *ngid = grent.gr_gid;
  1307. X        for (i = 0;grent.gr_mem[i] != (char *) 0;i++)
  1308. X            if (strcmp (grent.gr_mem[i], name) == 0)
  1309. X                return 0;
  1310. X
  1311. X        if (! (grent.gr_mem = (char **)
  1312. X                malloc (sizeof (char *) * (i + 2)))) {
  1313. X            fprintf (stderr, "%s: Out of Memory\n", Prog);
  1314. X            return -1;
  1315. X        }
  1316. X        memcpy (grent.gr_mem, grp->gr_mem, sizeof (char *) * (i + 2));
  1317. X        grent.gr_mem[i] = strdup (name);
  1318. X        grent.gr_mem[i + 1] = (char *) 0;
  1319. X
  1320. X        return ! gr_update (&grent);
  1321. X    }
  1322. X
  1323. X    /*
  1324. X     * The group did not exist, so I try to figure out what the
  1325. X     * GID is going to be.  The gid parameter is probably "", meaning
  1326. X     * I figure out the GID from the password file.  I want the UID
  1327. X     * and GID to match, unless the GID is already used.
  1328. X     */
  1329. X
  1330. X    if (gid[0] == '\0') {
  1331. X        i = 100;
  1332. X        for (pw_rewind ();pwd = pw_next ();) {
  1333. X            if (pwd->pw_uid >= i)
  1334. X                i = pwd->pw_uid + 1;
  1335. X        }
  1336. X        for (gr_rewind ();grp = gr_next ();) {
  1337. X            if (grp->gr_gid == i) {
  1338. X                i = -1;
  1339. X                break;
  1340. X            }
  1341. X        }
  1342. X    } else if (gid[0] >= '0' && gid[0] <= '9') {
  1343. X
  1344. X    /*
  1345. X     * The GID is a number, which means either this is a brand new
  1346. X     * group, or an existing group.  For existing groups I just add
  1347. X     * myself as a member, just like I did earlier.
  1348. X     */
  1349. X
  1350. X        i = atoi (gid);
  1351. X        for (gr_rewind ();grp = gr_next ();)
  1352. X            if (grp->gr_gid == i)
  1353. X                goto add_member;
  1354. X    } else
  1355. X
  1356. X    /*
  1357. X     * The last alternative is that the GID is a name which is not
  1358. X     * already the name of an existing group, and I need to figure
  1359. X     * out what group ID that group name is going to have.
  1360. X     */
  1361. X
  1362. X        i = -1;
  1363. X
  1364. X    /*
  1365. X     * If I don't have a group ID by now, I'll go get the
  1366. X     * next one.
  1367. X     */
  1368. X
  1369. X    if (i == -1) {
  1370. X        for (i = 100, gr_rewind ();grp = gr_next ();)
  1371. X            if (grp->gr_gid >= i)
  1372. X                i = grp->gr_gid + 1;
  1373. X    }
  1374. X
  1375. X    /*
  1376. X     * Now I have all of the fields required to create the new
  1377. X     * group.
  1378. X     */
  1379. X
  1380. X    if (gid[0] && (gid[0] <= '0' || gid[0] >= '9'))
  1381. X        grent.gr_name = gid;
  1382. X    else
  1383. X        grent.gr_name = name;
  1384. X
  1385. X    grent.gr_passwd = "!";
  1386. X    grent.gr_gid = i;
  1387. X    members[0] = name;
  1388. X    members[1] = (char *) 0;
  1389. X    grent.gr_mem = members;
  1390. X
  1391. X    *ngid = grent.gr_gid;
  1392. X    return ! gr_update (&grent);
  1393. X}
  1394. X
  1395. X/*
  1396. X * add_user - create a new user ID
  1397. X */
  1398. X
  1399. Xadd_user (name, uid, nuid, gid)
  1400. Xchar    *name;
  1401. Xchar    *uid;
  1402. Xint    *nuid;
  1403. Xint    gid;
  1404. X{
  1405. X    struct    passwd    *pwd;
  1406. X    struct    passwd    pwent;
  1407. X    int    i;
  1408. X
  1409. X    /*
  1410. X     * The first guess for the UID is either the numerical UID
  1411. X     * that the caller provided, or the next available UID.
  1412. X     */
  1413. X
  1414. X    if (uid[0] >= '0' && uid[0] <= '9') {
  1415. X        i = atoi (uid);
  1416. X    } if (uid[0] && (pwd = pw_locate (uid))) {
  1417. X        i = pwd->pw_uid;
  1418. X    } else {
  1419. X        i = 100;
  1420. X        for (pw_rewind ();pwd = pw_next ();)
  1421. X            if (pwd->pw_uid >= i)
  1422. X                i = pwd->pw_uid + 1;
  1423. X    }
  1424. X
  1425. X    /*
  1426. X     * I don't want to fill in the entire password structure
  1427. X     * members JUST YET, since there is still more data to be
  1428. X     * added.  So, I fill in the parts that I have.
  1429. X     */
  1430. X
  1431. X    pwent.pw_name = name;
  1432. X    pwent.pw_passwd = "!";
  1433. X#ifdef    ATT_AGE
  1434. X    pwent.pw_age = "";
  1435. X#endif
  1436. X#ifdef    ATT_COMMENT
  1437. X    pwent.pw_comment = "";
  1438. X#endif
  1439. X#ifdef    BSD_QUOTAS
  1440. X    pwent.pw_quota = 0;
  1441. X#endif
  1442. X    pwent.pw_uid = i;
  1443. X    pwent.pw_gid = gid;
  1444. X    pwent.pw_gecos = "";
  1445. X    pwent.pw_dir = "";
  1446. X    pwent.pw_shell = "";
  1447. X
  1448. X    *nuid = i;
  1449. X    return ! pw_update (&pwent);
  1450. X}
  1451. X
  1452. X/*
  1453. X * add_passwd - add or update the encrypted password
  1454. X */
  1455. X
  1456. Xadd_passwd (pwd, passwd)
  1457. Xstruct    passwd    *pwd;
  1458. Xchar    *passwd;
  1459. X{
  1460. X#ifdef    SHADOWPWD
  1461. X    struct    spwd    *sp;
  1462. X    struct    spwd    spent;
  1463. X#endif
  1464. X    static    char    newage[5];
  1465. X    extern    char    *l64a();
  1466. X
  1467. X    /*
  1468. X     * In the case of regular password files, this is real
  1469. X     * easy - pwd points to the entry in the password file.
  1470. X     * Shadow files are harder since there are zillions of
  1471. X     * things to do ...
  1472. X     */
  1473. X
  1474. X#ifndef    SHADOWPWD
  1475. X    pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
  1476. X#ifdef    ATT_AGE
  1477. X    if (strlen (pwd->pw_age) == 4) {
  1478. X        strcpy (newage, pwd->pw_age);
  1479. X        strcpy (newage + 2,
  1480. X            l64a (time ((long *) 0) / (7L*24L*3600L)));
  1481. X        pwd->pw_age = newage;
  1482. X    }
  1483. X#endif    /* ATT_AGE */
  1484. X    return 0;
  1485. X#else
  1486. X
  1487. X    /*
  1488. X     * Do the first and easiest shadow file case.  The user
  1489. X     * already exists in the shadow password file.
  1490. X     */
  1491. X
  1492. X    if (sp = spw_locate (pwd->pw_name)) {
  1493. X        spent = *sp;
  1494. X        spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
  1495. X        return ! spw_update (sp);
  1496. X    }
  1497. X
  1498. X    /*
  1499. X     * Pick the next easiest case - the user has an encrypted
  1500. X     * password which isn't equal to "!".  The password was set
  1501. X     * to "!" earlier when the entry was created, so this user
  1502. X     * would have to have had the password set someplace else.
  1503. X     */
  1504. X
  1505. X    if (strcmp (pwd->pw_passwd, "!") != 0) {
  1506. X        pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
  1507. X#ifdef    ATT_AGE
  1508. X        if (strlen (pwd->pw_age) == 4) {
  1509. X            strcpy (newage, pwd->pw_age);
  1510. X            strcpy (newage + 2,
  1511. X                l64a (time ((long *) 0) / (7L*24L*3600L)));
  1512. X            pwd->pw_age = newage;
  1513. X        }
  1514. X#endif    /* ATT_AGE */
  1515. X        return 0;
  1516. X    }
  1517. X
  1518. X    /*
  1519. X     * Now the really hard case - I need to create an entirely
  1520. X     * shadow password file entry.
  1521. X     */
  1522. X
  1523. X    spent.sp_namp = pwd->pw_name;
  1524. X    spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
  1525. X    spent.sp_lstchg = time ((long *) 0) / (24L*3600L);
  1526. X    spent.sp_min = getdef_num("PASS_MIN_DAYS", 0);
  1527. X                    /* 10000 is infinity this week */
  1528. X    spent.sp_max = getdef_num("PASS_MAX_DAYS", 10000);
  1529. X    spent.sp_warn = getdef_num("PASS_WARN_AGE", -1);
  1530. X    spent.sp_inact = -1;
  1531. X    spent.sp_expire = -1;
  1532. X    spent.sp_flag = -1;
  1533. X
  1534. X    return ! spw_update (&spent);
  1535. X#endif
  1536. X}
  1537. X
  1538. Xmain (argc, argv)
  1539. Xint    argc;
  1540. Xchar    **argv;
  1541. X{
  1542. X    char    buf[BUFSIZ];
  1543. X    char    *fields[8];
  1544. X    int    nfields;
  1545. X    char    *cp;
  1546. X#ifdef    SHADOWPWD
  1547. X    struct    spwd    *spw_locate();
  1548. X#endif
  1549. X    struct    passwd    *pw;
  1550. X    struct    passwd    newpw;
  1551. X    struct    passwd    *pw_locate();
  1552. X    int    errors = 0;
  1553. X    int    line = 0;
  1554. X    int    uid;
  1555. X    int    gid;
  1556. X    int    i;
  1557. X
  1558. X    if (Prog = strrchr (argv[0], '/'))
  1559. X        Prog++;
  1560. X    else
  1561. X        Prog = argv[0];
  1562. X
  1563. X    if (argc > 1 && argv[1][0] == '-')
  1564. X        usage ();
  1565. X
  1566. X    if (argc == 2) {
  1567. X        if (! freopen (argv[1], "r", stdin)) {
  1568. X            sprintf (buf, "%s: %s", Prog, argv[1]);
  1569. X            perror (buf);
  1570. X            exit (1);
  1571. X        }
  1572. X    }
  1573. X
  1574. X    /*
  1575. X     * Lock the password files and open them for update.  This will
  1576. X     * bring all of the entries into memory where they may be
  1577. X     * searched for an modified, or new entries added.  The password
  1578. X     * file is the key - if it gets locked, assume the others can
  1579. X     * be locked right away.
  1580. X     */
  1581. X
  1582. X    for (i = 0;i < 30;i++) {
  1583. X        if (pw_lock ())
  1584. X            break;
  1585. X    }
  1586. X    if (i == 30) {
  1587. X        fprintf (stderr, "%s: can't lock /etc/passwd.\n", Prog);
  1588. X        exit (1);
  1589. X    }
  1590. X#ifdef    SHADOWPWD
  1591. X    if (! spw_lock () || ! gr_lock ())
  1592. X#else
  1593. X    if (! gr_lock ())
  1594. X#endif
  1595. X    {
  1596. X        fprintf (stderr, "%s: can't lock files, try again later\n",
  1597. X            Prog);
  1598. X        (void) pw_unlock ();
  1599. X#ifdef    SHADOWPWD
  1600. X        (void) spw_unlock ();
  1601. X#endif
  1602. X        exit (1);
  1603. X    }
  1604. X#ifdef    SHADOWPWD
  1605. X    if (! pw_open (O_RDWR) || ! spw_open (O_RDWR) || ! gr_open (O_RDWR))
  1606. X#else
  1607. X    if (! pw_open (O_RDWR) || ! gr_open (O_RDWR))
  1608. X#endif
  1609. X    {
  1610. X        fprintf (stderr, "%s: can't open files\n", Prog);
  1611. X        (void) pw_unlock ();
  1612. X#ifdef    SHADOWPWD
  1613. X        (void) spw_unlock ();
  1614. X#endif
  1615. X        (void) gr_unlock ();
  1616. X        exit (1);
  1617. X    }
  1618. X
  1619. X    /*
  1620. X     * Read each line.  The line has the same format as a password
  1621. X     * file entry, except that certain fields are not contrained to
  1622. X     * be numerical values.  If a group ID is entered which does
  1623. X     * not already exist, an attempt is made to allocate the same
  1624. X     * group ID as the numerical user ID.  Should that fail, the
  1625. X     * next available group ID over 100 is allocated.  The pw_gid
  1626. X     * field will be updated with that value.
  1627. X     */
  1628. X
  1629. X    while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
  1630. X        line++;
  1631. X        if (cp = strrchr (buf, '\n')) {
  1632. X            *cp = '\0';
  1633. X        } else {
  1634. X            fprintf (stderr, "%s: line %d: line too long\n",
  1635. X                Prog, line);
  1636. X            errors++;
  1637. X            continue;
  1638. X        }
  1639. X
  1640. X        /*
  1641. X         * Break the string into fields and screw around with
  1642. X         * them.  There MUST be 7 colon separated fields,
  1643. X         * although the values aren't that particular.
  1644. X         */
  1645. X
  1646. X        for (cp = buf, nfields = 0;nfields < 7;nfields++) {
  1647. X            fields[nfields] = cp;
  1648. X            if (cp = strchr (cp, ':'))
  1649. X                *cp++ = '\0';
  1650. X            else
  1651. X                break;
  1652. X        }
  1653. X        if (*cp || nfields != 6) {
  1654. X            fprintf (stderr, "%s: line %d: invalid line\n",
  1655. X                Prog, line);
  1656. X            continue;
  1657. X        }
  1658. X
  1659. X        /*
  1660. X         * Now the fields are processed one by one.  The first
  1661. X         * field to be processed is the group name.  A new
  1662. X         * group will be created if the group name is non-numeric
  1663. X         * and does not already exist.  The named user will be
  1664. X         * the only member.  If there is no named group to be a
  1665. X         * member of, the UID will be figured out and that value
  1666. X         * will be a candidate for a new group, if that group ID
  1667. X         * exists, a whole new group ID will be made up.
  1668. X         */
  1669. X        
  1670. X        if (! (pw = pw_locate (fields[0])) &&
  1671. X            add_group (fields[0], fields[3], &gid)) {
  1672. X            fprintf (stderr, "%s: %d: can't create GID\n",
  1673. X                Prog, line);
  1674. X            errors++;
  1675. X            continue;
  1676. X        }
  1677. X
  1678. X        /*
  1679. X         * Now we work on the user ID.  It has to be specified
  1680. X         * either as a numerical value, or left blank.  If it
  1681. X         * is a numerical value, that value will be used, otherwise
  1682. X         * the next available user ID is computed and used.  After
  1683. X         * this there will at least be a (struct passwd) for the
  1684. X         * user.
  1685. X         */
  1686. X
  1687. X        if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
  1688. X            fprintf (stderr, "%s: line %d: can't create UID\n",
  1689. X                Prog, line);
  1690. X            errors++;
  1691. X            continue;
  1692. X        }
  1693. X
  1694. X        /*
  1695. X         * The password, gecos field, directory, and shell fields
  1696. X         * all come next.
  1697. X         */
  1698. X
  1699. X        if (! (pw = pw_locate (fields[0]))) {
  1700. X            fprintf (stderr, "%s: line %d: cannot find user %s\n",
  1701. X                Prog, line, fields[0]);
  1702. X            errors++;
  1703. X            continue;
  1704. X        }
  1705. X        newpw = *pw;
  1706. X
  1707. X        if (add_passwd (&newpw, fields[1])) {
  1708. X            fprintf (stderr, "%s: line %d: can't update password\n",
  1709. X                Prog, line);
  1710. X            errors++;
  1711. X            continue;
  1712. X        }
  1713. X        if (fields[4][0])
  1714. X            newpw.pw_gecos = fields[4];
  1715. X
  1716. X        if (fields[5][0])
  1717. X            newpw.pw_dir = fields[5];
  1718. X
  1719. X        if (fields[6][0])
  1720. X            newpw.pw_shell = fields[6];
  1721. X
  1722. X        if (newpw.pw_dir[0] && access (newpw.pw_dir, 0)) {
  1723. X            if (mkdir (newpw.pw_dir,
  1724. X                    0777 & ~getdef_num("UMASK", 0)))
  1725. X                fprintf (stderr, "%s: line %d: mkdir failed\n",
  1726. X                    Prog, line);
  1727. X            else if (chown (newpw.pw_dir,
  1728. X                    newpw.pw_uid, newpw.pw_gid))
  1729. X                fprintf (stderr, "%s: line %d: chown failed\n",
  1730. X                    Prog, line);
  1731. X        }
  1732. X
  1733. X        /*
  1734. X         * Update the password entry with the new changes made.
  1735. X         */
  1736. X
  1737. X        if (! pw_update (&newpw)) {
  1738. X            fprintf (stderr, "%s: line %d: can't update entry\n",
  1739. X                Prog, line);
  1740. X            errors++;
  1741. X            continue;
  1742. X        }
  1743. X    }
  1744. X
  1745. X    /*
  1746. X     * Any detected errors will cause the entire set of changes
  1747. X     * to be aborted.  Unlocking the password file will cause
  1748. X     * all of the changes to be ignored.  Otherwise the file is
  1749. X     * closed, causing the changes to be written out all at
  1750. X     * once, and then unlocked afterwards.
  1751. X     */
  1752. X
  1753. X    if (errors) {
  1754. X        fprintf (stderr, "%s: error detected, changes ignored\n", Prog);
  1755. X        (void) gr_unlock ();
  1756. X#ifdef    SHADOWPWD
  1757. X        (void) spw_unlock ();
  1758. X#endif
  1759. X        (void) pw_unlock ();
  1760. X        exit (1);
  1761. X    }
  1762. X#ifdef    SHADOWPWD
  1763. X    if (! pw_close () || ! spw_close () || ! gr_close ())
  1764. X#else
  1765. X    if (! pw_close () || ! gr_close ())
  1766. X#endif
  1767. X    {
  1768. X        fprintf (stderr, "%s: error updating files\n", Prog);
  1769. X        (void) gr_unlock ();
  1770. X#ifdef    SHADOWPWD
  1771. X        (void) spw_unlock ();
  1772. X#endif
  1773. X        (void) pw_unlock ();
  1774. X        exit (1);
  1775. X    }
  1776. X    (void) gr_unlock ();
  1777. X#ifdef    SHADOWPWD
  1778. X    (void) spw_unlock ();
  1779. X#endif
  1780. X    (void) pw_unlock ();
  1781. X
  1782. X    exit (0);
  1783. X    /*NOTREACHED*/
  1784. X}
  1785. END_OF_FILE
  1786.   if test 13144 -ne `wc -c <'newusers.c'`; then
  1787.     echo shar: \"'newusers.c'\" unpacked with wrong size!
  1788.   fi
  1789.   # end of 'newusers.c'
  1790. fi
  1791. if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  1792.   echo shar: Will not clobber existing file \"'patchlevel.h'\"
  1793. else
  1794.   echo shar: Extracting \"'patchlevel.h'\" \(431 characters\)
  1795.   sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
  1796. X/*
  1797. X * Copyright 1991, John F. Haugh II
  1798. X * All rights reserved.
  1799. X *
  1800. X * Permission is granted to copy and create derivative works for any
  1801. X * non-commercial purpose, provided this copyright notice is preserved
  1802. X * in all copies of source code, or included in human readable form
  1803. X * and conspicuously displayed on all copies of object code or
  1804. X * distribution media.
  1805. X */
  1806. X
  1807. X#define    RELEASE        3
  1808. X#define    PATCHLEVEL    13
  1809. X#define    VERSION        "3.1.0"
  1810. END_OF_FILE
  1811.   if test 431 -ne `wc -c <'patchlevel.h'`; then
  1812.     echo shar: \"'patchlevel.h'\" unpacked with wrong size!
  1813.   fi
  1814.   # end of 'patchlevel.h'
  1815. fi
  1816. if test -f 'sgroupio.c' -a "${1}" != "-c" ; then 
  1817.   echo shar: Will not clobber existing file \"'sgroupio.c'\"
  1818. else
  1819.   echo shar: Extracting \"'sgroupio.c'\" \(11654 characters\)
  1820.   sed "s/^X//" >'sgroupio.c' <<'END_OF_FILE'
  1821. X/*
  1822. X * Copyright 1990, 1991, John F. Haugh II
  1823. X * All rights reserved.
  1824. X *
  1825. X * Permission is granted to copy and create derivative works for any
  1826. X * non-commercial purpose, provided this copyright notice is preserved
  1827. X * in all copies of source code, or included in human readable form
  1828. X * and conspicuously displayed on all copies of object code or
  1829. X * distribution media.
  1830. X *
  1831. X *    This file implements a transaction oriented shadow group
  1832. X *    database library.  The shadow group file is updated one
  1833. X *    entry at a time.  After each transaction the file must be
  1834. X *    logically closed and transferred to the existing shadow
  1835. X *    group file.  The sequence of events is
  1836. X *
  1837. X *    sgr_lock            -- lock shadow group file
  1838. X *    sgr_open            -- logically open shadow group file
  1839. X *    while transaction to process
  1840. X *        sgr_(locate,update,remove) -- perform transaction
  1841. X *    done
  1842. X *    sgr_close            -- commit transactions
  1843. X *    sgr_unlock            -- remove shadow group lock
  1844. X */
  1845. X
  1846. X#include <sys/types.h>
  1847. X#include <sys/stat.h>
  1848. X#include <fcntl.h>
  1849. X#include <errno.h>
  1850. X#include <stdio.h>
  1851. X#ifdef    BSD
  1852. X#include <strings.h>
  1853. X#define    strchr    index
  1854. X#define    strrchr    rindex
  1855. X#else
  1856. X#include <string.h>
  1857. X#endif
  1858. X#include "shadow.h"
  1859. X
  1860. X#ifndef    lint
  1861. Xstatic    char    sccsid[] = "@(#)sgroupio.c    3.6    09:10:42    7/17/91";
  1862. X#endif
  1863. X
  1864. Xstatic    int    islocked;
  1865. Xstatic    int    isopen;
  1866. Xstatic    int    open_modes;
  1867. Xstatic    FILE    *sgrfp;
  1868. X
  1869. Xstruct    sg_file_entry {
  1870. X    char    *sgr_line;
  1871. X    int    sgr_changed;
  1872. X    struct    sgrp    *sgr_entry;
  1873. X    struct    sg_file_entry *sgr_next;
  1874. X};
  1875. X
  1876. Xstatic    struct    sg_file_entry    *sgr_head;
  1877. Xstatic    struct    sg_file_entry    *sgr_tail;
  1878. Xstatic    struct    sg_file_entry    *sgr_cursor;
  1879. Xstatic    int    sgr_changed;
  1880. Xstatic    int    lock_pid;
  1881. X
  1882. X#define    SG_LOCK    "/etc/gshadow.lock"
  1883. X#define    GR_TEMP "/etc/gshadow.%d"
  1884. X#define    SGROUP    "/etc/gshadow"
  1885. X
  1886. Xstatic    char    sg_filename[BUFSIZ] = SGROUP;
  1887. X
  1888. Xextern    char    *strdup();
  1889. Xextern    struct    sgrp    *sgetsgent();
  1890. Xextern    char    *fgetsx();
  1891. Xextern    char    *malloc();
  1892. X
  1893. X/*
  1894. X * sgr_dup - duplicate a shadow group file entry
  1895. X *
  1896. X *    sgr_dup() accepts a pointer to a shadow group file entry and
  1897. X *    returns a pointer to a shadow group file entry in allocated memory.
  1898. X */
  1899. X
  1900. Xstatic struct sgrp *
  1901. Xsgr_dup (sgrent)
  1902. Xstruct    sgrp    *sgrent;
  1903. X{
  1904. X    struct    sgrp    *sgr;
  1905. X    int    i;
  1906. X
  1907. X    if (! (sgr = (struct sgrp *) malloc (sizeof *sgr)))
  1908. X        return 0;
  1909. X
  1910. X    if ((sgr->sg_name = strdup (sgrent->sg_name)) == 0 ||
  1911. X            (sgr->sg_passwd = strdup (sgrent->sg_passwd)) == 0)
  1912. X        return 0;
  1913. X
  1914. X    for (i = 0;sgrent->sg_mem[i];i++)
  1915. X        ;
  1916. X
  1917. X    sgr->sg_mem = (char **) malloc (sizeof (char *) * (i + 1));
  1918. X    for (i = 0;sgrent->sg_mem[i];i++)
  1919. X        if (! (sgr->sg_mem[i] = strdup (sgrent->sg_mem[i])))
  1920. X            return 0;
  1921. X
  1922. X    sgr->sg_mem[i] = 0;
  1923. X
  1924. X    sgr->sg_adm = (char **) malloc (sizeof (char *) * (i + 1));
  1925. X    for (i = 0;sgrent->sg_adm[i];i++)
  1926. X        if (! (sgr->sg_adm[i] = strdup (sgrent->sg_adm[i])))
  1927. X            return 0;
  1928. X
  1929. X    sgr->sg_adm[i] = 0;
  1930. X
  1931. X    return sgr;
  1932. X}
  1933. X
  1934. X/*
  1935. X * sgr_free - free a dynamically allocated shadow group file entry
  1936. X *
  1937. X *    sgr_free() frees up the memory which was allocated for the
  1938. X *    pointed to entry.
  1939. X */
  1940. X
  1941. Xstatic void
  1942. Xsgr_free (sgrent)
  1943. Xstruct    sgrp    *sgrent;
  1944. X{
  1945. X    int    i;
  1946. X
  1947. X    free (sgrent->sg_name);
  1948. X    free (sgrent->sg_passwd);
  1949. X
  1950. X    for (i = 0;sgrent->sg_mem[i];i++)
  1951. X        free (sgrent->sg_mem[i]);
  1952. X
  1953. X    free (sgrent->sg_mem);
  1954. X
  1955. X    for (i = 0;sgrent->sg_adm[i];i++)
  1956. X        free (sgrent->sg_adm[i]);
  1957. X
  1958. X    free (sgrent->sg_adm);
  1959. X}
  1960. X
  1961. X/*
  1962. X * sgr_name - change the name of the shadow group file
  1963. X */
  1964. X
  1965. Xint
  1966. Xsgr_name (name)
  1967. Xchar    *name;
  1968. X{
  1969. X    if (isopen || strlen (name) > (BUFSIZ-10))
  1970. X        return -1;
  1971. X
  1972. X    strcpy (sg_filename, name);
  1973. X    return 0;
  1974. X}
  1975. X
  1976. X/*
  1977. X * sgr_lock - lock a shadow group file
  1978. X *
  1979. X *    sgr_lock() encapsulates the lock operation.  it returns
  1980. X *    TRUE or FALSE depending on the shadow group file being
  1981. X *    properly locked.  the lock is set by creating a semaphore
  1982. X *    file, SG_LOCK.
  1983. X */
  1984. X
  1985. Xint
  1986. Xsgr_lock ()
  1987. X{
  1988. X    int    fd;
  1989. X    int    pid;
  1990. X    int    len;
  1991. X    char    file[BUFSIZ];
  1992. X    char    buf[32];
  1993. X    struct    stat    sb;
  1994. X
  1995. X    if (islocked)
  1996. X        return 1;
  1997. X
  1998. X    if (strcmp (sg_filename, SGROUP) != 0)
  1999. X        return 0;
  2000. X
  2001. X    /*
  2002. X     * Create a lock file which can be switched into place
  2003. X     */
  2004. X
  2005. X    sprintf (file, GR_TEMP, lock_pid = getpid ());
  2006. X    if ((fd = open (file, O_CREAT|O_EXCL|O_WRONLY, 0600)) == -1)
  2007. X        return 0;
  2008. X
  2009. X    sprintf (buf, "%d", lock_pid);
  2010. X    if (write (fd, buf, strlen (buf) + 1) != strlen (buf) + 1) {
  2011. X        (void) close (fd);
  2012. X        (void) unlink (file);
  2013. X        return 0;
  2014. X    }
  2015. X    close (fd);
  2016. X
  2017. X    /*
  2018. X     * Simple case first -
  2019. X     *    Link fails (in a sane environment ...) if the target
  2020. X     *    exists already.  So we try to switch in a new lock
  2021. X     *    file.  If that succeeds, we assume we have the only
  2022. X     *    valid lock.  Needs work for NFS where this assumption
  2023. X     *    may not hold.  The simple hack is to check the link
  2024. X     *    count on the source file, which should be 2 iff the
  2025. X     *    link =really= worked.
  2026. X     */
  2027. X
  2028. X    if (link (file, SG_LOCK) == 0) {
  2029. X        if (stat (file, &sb) != 0)
  2030. X            return 0;
  2031. X
  2032. X        if (sb.st_nlink != 2)
  2033. X            return 0;
  2034. X
  2035. X        (void) unlink (file);
  2036. X        islocked = 1;
  2037. X        return 1;
  2038. X    }
  2039. X
  2040. X    /*
  2041. X     * Invalid lock test -
  2042. X     *    Open the lock file and see if the lock is valid.
  2043. X     *    The PID of the lock file is checked, and if the PID
  2044. X     *    is not valid, the lock file is removed.  If the unlink
  2045. X     *    of the lock file fails, it should mean that someone
  2046. X     *    else is executing this code.  They will get success,
  2047. X     *    and we will fail.
  2048. X     */
  2049. X
  2050. X    if ((fd = open (SG_LOCK, O_RDWR)) == -1 ||
  2051. X            (len = read (fd, buf, BUFSIZ)) <= 0) {
  2052. X        errno = EINVAL;
  2053. X        return 0;
  2054. X    }
  2055. X    buf[len] = '\0';
  2056. X    if ((pid = strtol (buf, (char **) 0, 10)) == 0) {
  2057. X        errno = EINVAL;
  2058. X        return 0;
  2059. X    }
  2060. X    if (kill (pid, 0) == 0)  {
  2061. X        errno = EEXIST;
  2062. X        return 0;
  2063. X    }
  2064. X    if (unlink (SG_LOCK)) {
  2065. X        (void) close (fd);
  2066. X        (void) unlink (file);
  2067. X
  2068. X        return 0;
  2069. X    }
  2070. X
  2071. X    /*
  2072. X     * Re-try lock -
  2073. X     *    The invalid lock has now been removed and I should
  2074. X     *    be able to acquire a lock for myself just fine.  If
  2075. X     *    this fails there will be no retry.  The link count
  2076. X     *    test here makes certain someone executing the previous
  2077. X     *    block of code didn't just remove the lock we just
  2078. X     *    linked to.
  2079. X     */
  2080. X
  2081. X    if (link (file, SG_LOCK) == 0) {
  2082. X        if (stat (file, &sb) != 0)
  2083. X            return 0;
  2084. X
  2085. X        if (sb.st_nlink != 2)
  2086. X            return 0;
  2087. X
  2088. X        (void) unlink (file);
  2089. X        islocked = 1;
  2090. X        return 1;
  2091. X    }
  2092. X    (void) unlink (file);
  2093. X    return 0;
  2094. X}
  2095. X
  2096. X/*
  2097. X * sgr_unlock - logically unlock a shadow group file
  2098. X *
  2099. X *    sgr_unlock() removes the lock which was set by an earlier
  2100. X *    invocation of sgr_lock().
  2101. X */
  2102. X
  2103. Xint
  2104. Xsgr_unlock ()
  2105. X{
  2106. X    if (isopen) {
  2107. X        open_modes = O_RDONLY;
  2108. X        if (! sgr_close ())
  2109. X            return 0;
  2110. X    }
  2111. X    if (islocked) {
  2112. X        islocked = 0;
  2113. X        if (lock_pid != getpid ())
  2114. X            return 0;
  2115. X
  2116. X        (void) unlink (SG_LOCK);
  2117. X        return 1;
  2118. X    }
  2119. X    return 0;
  2120. X}
  2121. X
  2122. X/*
  2123. X * sgr_open - open a shadow group file
  2124. X *
  2125. X *    sgr_open() encapsulates the open operation.  it returns
  2126. X *    TRUE or FALSE depending on the shadow group file being
  2127. X *    properly opened.
  2128. X */
  2129. X
  2130. Xint
  2131. Xsgr_open (mode)
  2132. Xint    mode;
  2133. X{
  2134. X    char    buf[8192];
  2135. X    char    *cp;
  2136. X    struct    sg_file_entry    *sgrf;
  2137. X    struct    sgrp    *sgrent;
  2138. X
  2139. X    if (isopen || (mode != O_RDONLY && mode != O_RDWR))
  2140. X        return 0;
  2141. X
  2142. X    if (mode != O_RDONLY && ! islocked &&
  2143. X            strcmp (sg_filename, SGROUP) == 0)
  2144. X        return 0;
  2145. X
  2146. X    if ((sgrfp = fopen (sg_filename, mode == O_RDONLY ? "r":"r+")) == 0)
  2147. X        return 0;
  2148. X
  2149. X    sgr_head = sgr_tail = sgr_cursor = 0;
  2150. X    sgr_changed = 0;
  2151. X
  2152. X    while (fgetsx (buf, sizeof buf, sgrfp) != (char *) 0) {
  2153. X        if (cp = strrchr (buf, '\n'))
  2154. X            *cp = '\0';
  2155. X
  2156. X        if (! (sgrf = (struct sg_file_entry *) malloc (sizeof *sgrf)))
  2157. X            return 0;
  2158. X
  2159. X        sgrf->sgr_changed = 0;
  2160. X        sgrf->sgr_line = strdup (buf);
  2161. X        if ((sgrent = sgetsgent (buf)) && ! (sgrent = sgr_dup (sgrent)))
  2162. X            return 0;
  2163. X
  2164. X        sgrf->sgr_entry = sgrent;
  2165. X
  2166. X        if (sgr_head == 0) {
  2167. X            sgr_head = sgr_tail = sgrf;
  2168. X            sgrf->sgr_next = 0;
  2169. X        } else {
  2170. X            sgr_tail->sgr_next = sgrf;
  2171. X            sgrf->sgr_next = 0;
  2172. X            sgr_tail = sgrf;
  2173. X        }
  2174. X    }
  2175. X    isopen++;
  2176. X    open_modes = mode;
  2177. X
  2178. X    return 1;
  2179. X}
  2180. X
  2181. X/*
  2182. X * sgr_close - close the shadow group file
  2183. X *
  2184. X *    sgr_close() outputs any modified shadow group file entries and
  2185. X *    frees any allocated memory.
  2186. X */
  2187. X
  2188. Xint
  2189. Xsgr_close ()
  2190. X{
  2191. X    char    backup[BUFSIZ];
  2192. X    int    mask;
  2193. X    int    c;
  2194. X    int    errors = 0;
  2195. X    FILE    *bkfp;
  2196. X    struct    sg_file_entry *sgrf;
  2197. X    struct    stat    sb;
  2198. X
  2199. X    if (! isopen) {
  2200. X        errno = EINVAL;
  2201. X        return 0;
  2202. X    }
  2203. X    if (islocked && lock_pid != getpid ()) {
  2204. X        isopen = 0;
  2205. X        islocked = 0;
  2206. X        errno = EACCES;
  2207. X        return 0;
  2208. X    }
  2209. X    strcpy (backup, sg_filename);
  2210. X    strcat (backup, "-");
  2211. X
  2212. X    if (open_modes == O_RDWR && sgr_changed) {
  2213. X        mask = umask (0277);
  2214. X        (void) chmod (backup, 0400);
  2215. X        if ((bkfp = fopen (backup, "w")) == 0) {
  2216. X            umask (mask);
  2217. X            return 0;
  2218. X        }
  2219. X        umask (mask);
  2220. X        fstat (fileno (sgrfp), &sb);
  2221. X        chown (backup, sb.st_uid, sb.st_gid);
  2222. X
  2223. X        rewind (sgrfp);
  2224. X        while ((c = getc (sgrfp)) != EOF) {
  2225. X            if (putc (c, bkfp) == EOF) {
  2226. X                fclose (bkfp);
  2227. X                return 0;
  2228. X            }
  2229. X        }
  2230. X        if (fclose (bkfp))
  2231. X            return 0;
  2232. X
  2233. X        isopen = 0;
  2234. X        (void) fclose (sgrfp);
  2235. X
  2236. X        mask = umask (0277);
  2237. X        (void) chmod (sg_filename, 0400);
  2238. X        if (! (sgrfp = fopen (sg_filename, "w"))) {
  2239. X            umask (mask);
  2240. X            return 0;
  2241. X        }
  2242. X        umask (mask);
  2243. X
  2244. X        for (sgrf = sgr_head;! errors && sgrf;sgrf = sgrf->sgr_next) {
  2245. X            if (sgrf->sgr_changed) {
  2246. X                if (putsgent (sgrf->sgr_entry, sgrfp))
  2247. X                    errors++;
  2248. X            } else {
  2249. X                if (fputsx (sgrf->sgr_line, sgrfp))
  2250. X                    errors++;
  2251. X
  2252. X                if (putc ('\n', sgrfp) == EOF)
  2253. X                    errors++;
  2254. X            }
  2255. X        }
  2256. X        if (fflush (sgrfp))
  2257. X            errors++;
  2258. X
  2259. X        if (errors) {
  2260. X            unlink (sg_filename);
  2261. X            link (backup, sg_filename);
  2262. X            unlink (backup);
  2263. X            return 0;
  2264. X        }
  2265. X    }
  2266. X    if (fclose (sgrfp))
  2267. X        return 0;
  2268. X
  2269. X    sgrfp = 0;
  2270. X
  2271. X    while (sgr_head != 0) {
  2272. X        sgrf = sgr_head;
  2273. X        sgr_head = sgrf->sgr_next;
  2274. X
  2275. X        if (sgrf->sgr_entry) {
  2276. X            sgr_free (sgrf->sgr_entry);
  2277. X            free (sgrf->sgr_entry);
  2278. X        }
  2279. X        if (sgrf->sgr_line)
  2280. X            free (sgrf->sgr_line);
  2281. X
  2282. X        free (sgrf);
  2283. X    }
  2284. X    sgr_tail = 0;
  2285. X    isopen = 0;
  2286. X    return 1;
  2287. X}
  2288. X
  2289. Xint
  2290. Xsgr_update (sgrent)
  2291. Xstruct    sgrp    *sgrent;
  2292. X{
  2293. X    struct    sg_file_entry    *sgrf;
  2294. X    struct    sgrp    *nsgr;
  2295. X
  2296. X    if (! isopen || open_modes == O_RDONLY) {
  2297. X        errno = EINVAL;
  2298. X        return 0;
  2299. X    }
  2300. X    for (sgrf = sgr_head;sgrf != 0;sgrf = sgrf->sgr_next) {
  2301. X        if (sgrf->sgr_entry == 0)
  2302. X            continue;
  2303. X
  2304. X        if (strcmp (sgrent->sg_name, sgrf->sgr_entry->sg_name) != 0)
  2305. X            continue;
  2306. X
  2307. X        if (! (nsgr = sgr_dup (sgrent)))
  2308. X            return 0;
  2309. X        else {
  2310. X            sgr_free (sgrf->sgr_entry);
  2311. X            *(sgrf->sgr_entry) = *nsgr;
  2312. X        }
  2313. X        sgrf->sgr_changed = 1;
  2314. X        sgr_cursor = sgrf;
  2315. X        return sgr_changed = 1;
  2316. X    }
  2317. X    sgrf = (struct sg_file_entry *) malloc (sizeof *sgrf);
  2318. X    if (! (sgrf->sgr_entry = sgr_dup (sgrent)))
  2319. X        return 0;
  2320. X
  2321. X    sgrf->sgr_changed = 1;
  2322. X    sgrf->sgr_next = 0;
  2323. X    sgrf->sgr_line = 0;
  2324. X
  2325. X    if (sgr_tail)
  2326. X        sgr_tail->sgr_next = sgrf;
  2327. X
  2328. X    if (! sgr_head)
  2329. X        sgr_head = sgrf;
  2330. X
  2331. X    sgr_tail = sgrf;
  2332. X
  2333. X    return sgr_changed = 1;
  2334. X}
  2335. X
  2336. Xint
  2337. Xsgr_remove (name)
  2338. Xchar    *name;
  2339. X{
  2340. X    struct    sg_file_entry    *sgrf;
  2341. X    struct    sg_file_entry    *osgrf;
  2342. X
  2343. X    if (! isopen || open_modes == O_RDONLY) {
  2344. X        errno = EINVAL;
  2345. X        return 0;
  2346. X    }
  2347. X    for (osgrf = 0, sgrf = sgr_head;sgrf != 0;
  2348. X            osgrf = sgrf, sgrf = sgrf->sgr_next) {
  2349. X        if (! sgrf->sgr_entry)
  2350. X            continue;
  2351. X
  2352. X        if (strcmp (name, sgrf->sgr_entry->sg_name) != 0)
  2353. X            continue;
  2354. X
  2355. X        if (sgrf == sgr_cursor)
  2356. X            sgr_cursor = osgrf;
  2357. X
  2358. X        if (osgrf != 0)
  2359. X            osgrf->sgr_next = sgrf->sgr_next;
  2360. X        else
  2361. X            sgr_head = sgrf->sgr_next;
  2362. X
  2363. X        if (sgrf == sgr_tail)
  2364. X            sgr_tail = osgrf;
  2365. X
  2366. X        return sgr_changed = 1;
  2367. X    }
  2368. X    errno = ENOENT;
  2369. X    return 0;
  2370. X}
  2371. X
  2372. Xstruct sgrp *
  2373. Xsgr_locate (name)
  2374. Xchar    *name;
  2375. X{
  2376. X    struct    sg_file_entry    *sgrf;
  2377. X
  2378. X    if (! isopen) {
  2379. X        errno = EINVAL;
  2380. X        return 0;
  2381. X    }
  2382. X    for (sgrf = sgr_head;sgrf != 0;sgrf = sgrf->sgr_next) {
  2383. X        if (sgrf->sgr_entry == 0)
  2384. X            continue;
  2385. X
  2386. X        if (strcmp (name, sgrf->sgr_entry->sg_name) == 0) {
  2387. X            sgr_cursor = sgrf;
  2388. X            return sgrf->sgr_entry;
  2389. X        }
  2390. X    }
  2391. X    errno = ENOENT;
  2392. X    return 0;
  2393. X}
  2394. X
  2395. Xint
  2396. Xsgr_rewind ()
  2397. X{
  2398. X    if (! isopen) {
  2399. X        errno = EINVAL;
  2400. X        return 0;
  2401. X    }
  2402. X    sgr_cursor = 0;
  2403. X    return 1;
  2404. X}
  2405. X
  2406. Xstruct sgrp *
  2407. Xsgr_next ()
  2408. X{
  2409. X    if (! isopen) {
  2410. X        errno = EINVAL;
  2411. X        return 0;
  2412. X    }
  2413. X    if (sgr_cursor == 0)
  2414. X        sgr_cursor = sgr_head;
  2415. X    else
  2416. X        sgr_cursor = sgr_cursor->sgr_next;
  2417. X
  2418. X    while (sgr_cursor) {
  2419. X        if (sgr_cursor->sgr_entry)
  2420. X            return sgr_cursor->sgr_entry;
  2421. X
  2422. X        sgr_cursor = sgr_cursor->sgr_next;
  2423. X    }
  2424. X    return 0;
  2425. X}
  2426. END_OF_FILE
  2427.   if test 11654 -ne `wc -c <'sgroupio.c'`; then
  2428.     echo shar: \"'sgroupio.c'\" unpacked with wrong size!
  2429.   fi
  2430.   # end of 'sgroupio.c'
  2431. fi
  2432. echo shar: End of archive 4 \(of 11\).
  2433. cp /dev/null ark4isdone
  2434. MISSING=""
  2435. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  2436.     if test ! -f ark${I}isdone ; then
  2437.     MISSING="${MISSING} ${I}"
  2438.     fi
  2439. done
  2440. if test "${MISSING}" = "" ; then
  2441.     echo You have unpacked all 11 archives.
  2442.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  2443. else
  2444.     echo You still must unpack the following archives:
  2445.     echo "        " ${MISSING}
  2446. fi
  2447. exit 0
  2448. exit 0 # Just in case...
  2449.